package gbench.appdemo.ggplot.layer;

import static gbench.common.tree.LittleTree.IRecord.aaclc;

import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;

import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.data.DataReader.DFrame;
import gbench.common.matlib.matrix.MatrixOps.Tuple2;
import gbench.common.tree.LittleTree.IRecord;
import gbench.appdemo.ggplot.aes.Aesthetics;
import gbench.appdemo.ggplot.function.Fx;
import gbench.appdemo.ggplot.function.Fxy;
import gbench.appdemo.ggplot.plot.panel.SinkPanel;

/**
 * 一个 Layer 就是一种绘图方法geom-stats的集合。 基础结构是 是 aes 和 源数据
 * 
 * @author gbench
 *
 */
public abstract class Layer {

    /**
     * 
     * @param mappings
     * @param dataframe
     */
    public Layer(Aesthetics mappings, DFrame data) {
        this.aesthetics = mappings;
        this.data = data;
    }

    /**
     * 获取绘图映射（配置）
     * 
     * @param <T> 默认的值类型
     * @param name 属性名
     * @param defaultValue 默认值
     * @return 绘图映射（配置）
     */
    public <T> T getAes(String name, T defaultValue) {
        return aesthetics.get(name, defaultValue);
    }
    
    /**
     * 从aesthetics中提取映射数据
     * @param <T> 映射结果
     * @param evaluator 计算函数
     * @return T类型的数据结果
     */
    public <T> T getAes(Function<IRecord,T> evaluator) {
        return evaluator.apply(aesthetics.getMappings());
    }
    
    /**
     * 提取映射的值<br><br>
     * 首先尝试从 aesthetics 提取<br>
     * - 如果提取到字符类型，是该字符串为键名变换,使用该键名 从 data中提取 数据向量。<br>
     * - 如果提取失败 则从 data中提取. <br>
     * @param name 键（属性）名
     * @return NVec
     */
    public NVec get(final String name) {
        final var obj = aesthetics.get(name);
        if (obj instanceof Range) { // 区间范围
            return NVec.of((Range) obj);
        } else if (obj instanceof NVec) {
            return (NVec) obj;
        } else if (obj instanceof Fx) { // 一元函数
            final var mapper = (Fx) obj;
            final var xobj = this.get("x");
            final var nvex = (xobj instanceof NVec ? (NVec) xobj : data.get("x"));
            return nvex.fmapN(mapper);
        } else if (obj instanceof Fxy) { // 二元函数
            final var mapper = (Fxy) obj;
            final var xobj = this.get("x");
            final var nvex = (xobj instanceof NVec ? (NVec) xobj : data.get("x"));
            final var yobj = this.get("y");
            final var nvey = (yobj instanceof NVec ? (NVec) yobj : data.get("y"));
            return Tuple2.zip(nvex.data(), nvey.data()).map(t -> mapper.apply(t._1(), t._2())).collect(aaclc(NVec::new));
        } else if (obj instanceof Number) { // 数字类型
            return NVec.of(((Number) obj).intValue());
        } else if (obj instanceof Number[]) { // 数字数组类型
            return NVec.of(((Number[]) obj));
        } else if (obj instanceof Collection) { // 集合类型
            return ((Collection<?>) obj).stream().map(IRecord.coerce(Double.class)).collect(IRecord.aaclc((NVec::new)));
        } else if (obj instanceof Stream) { // 集合类型
            return ((Stream<?>) obj).map(IRecord.coerce(Double.class)).collect(IRecord.aaclc((NVec::new)));
        } else if (obj instanceof String) { // 字符串类型，说明这是一个键名变换，使用变换键名 obj在data 中进行数据检索
            if(data==null)return null;
            return data.get((String) obj);
        } else {
            return this.data == null ? null : this.data.get(name);
        }
    }

    /**
     * 从aesthetics中提取映射数据
     * @param <T> 映射结果
     * @param evaluator 计算函数
     * @return T类型的数据结果
     */
    public <T> T get(Function<IRecord,T> evaluator) {
        if(this.data==null)return null;
        return this.data.eval(evaluator);
    }

    /**
     * @return the mappings
     */
    public Aesthetics getAesthetics() {
        return aesthetics;
    }

    /**
     * @param aesthetics the mappings to set
     */
    public void setAesthetics(Aesthetics aesthetics) {
        this.aesthetics = aesthetics;
    }

    /**
     * @return the dataframe
     */
    public DFrame getData() {
        return data;
    }

    /**
     * @param dataframe the dataframe to set
     */
    public void setData(DFrame dataframe) {
        this.data = dataframe;
    }

    /**
     * 图层计算
     * 
     * @return
     */
    public Stream<NVec> evaluate2(SinkPanel panel) {

        return this.evaluate(panel).map(e -> {
            final var coord = e._1(); // 坐标
            final var rgb = e._2(); // 颜色
            return NVec.of(coord._1(), coord._2(), rgb[0], rgb[1], rgb[2]);
        });
    }
    
    /**
     * 是否是覆盖层：覆盖层不参与缩放框的编辑
     * @return 是否是覆盖层 true覆盖层,false 不是覆盖层
     */
    public boolean isOverlayer() {
        return false;
    }

    /**
     * 图层计算
     * 
     * @param panel 计算面板
     * @return 像素点数据
     */
    public abstract Stream<Tuple2<Tuple2<Double, Double>, int[]>> evaluate(SinkPanel panel);

    protected Aesthetics aesthetics;
    protected DFrame data;
}
